﻿using Tesla.Bounding;
using Tesla.Content;
using Tesla.Core;
using Tesla.Graphics;
using Tesla.Input;
using Tesla.Math;
using Tesla.Scene;

namespace TeslaSamples.Misc {
    [AppDescription("Input Handling", PlatformCapabilities.Direct3D10 | PlatformCapabilities.XNA4, "UserInputImage", "UserInputDesc", "UserInputSource")]
    public class UserInput : BasicApp {
        private float rotateSpeed;
        private float speed;
        private InputLayer input;

        protected override void LoadContent() {
            Window.Title = "Input Handling Sample";
            rotateSpeed = 2.5f;
            speed = 105.0f;

            Renderer.CurrentCamera.Position = new Vector3(0, 800, 100);
            Renderer.CurrentCamera.LookAt(Vector3.Zero, Vector3.Up);
            Renderer.CurrentCamera.Update();

            //Load our fancy pants racecar 
            ModelLoaderParameters mlp = new ModelLoaderParameters();
            mlp.SwapWindingOrder = true;
            mlp.NormalGeneration = NormalGeneration.Crease;
            mlp.PreferLitMaterials = true;

            Spatial car = ContentManager.Load<Spatial>("Models//Jeep.dxs", mlp);
            car.SetModelBound(new BoundingBox());
            RootNode.AddChild(car);

            //Create an input layer, this layer will hold all of our controls and input response to the arrow keys, moving the car
            input = new InputLayer();

            //Input layers can be disabled or enabled, so you're able to logically group your inputs together and treat them as one unit.
            //E.g. if you have a screen menu and want the mouse enabled, but for gameplay do not want the mouse enabled.
            //Rather than disabling the whole layer, we'll just disable the first 5 triggers - which are the
            //FPS controls set in BasicApp.
            InputLayer.EnableTrigger(0, false);
            InputLayer.EnableTrigger(1, false);
            InputLayer.EnableTrigger(2, false);
            InputLayer.EnableTrigger(3, false);
            InputLayer.EnableTrigger(4, false);

            //Since we disabled the basicApp's input alyer, lets make sure we can get out of the app
            input.RegisterTrigger(new InputTrigger(new KeyPressedCondition(Keys.Escape, false),
                new InputAction(delegate(GameTime time) {
                    Exit();
                })));

            input.RegisterTrigger(new InputTrigger(new KeyPressedCondition(Keys.A, true), new InputAction(
                delegate(GameTime time) {
                    car.Rotation *= Quaternion.FromAngleAxis(rotateSpeed * (float)time.ElapsedTimeInSeconds, Vector3.Up);
                }
            )));
            input.RegisterTrigger(new InputTrigger(new KeyPressedCondition(Keys.D, true), new InputAction(
                delegate(GameTime time) {
                    car.Rotation *= Quaternion.FromAngleAxis(-rotateSpeed * (float) time.ElapsedTimeInSeconds, Vector3.Up);
                }
            )));

            input.RegisterTrigger(new InputTrigger(new KeyPressedCondition(Keys.W, true), new InputAction(
                delegate(GameTime time) {
                    //Usually a good idea to use the world properties, since this may be attached to a node that is moving also
                    car.Translation -= car.WorldTransform.GetRotationVector(2) * speed * (float) time.ElapsedTimeInSeconds;
                }
            )));
            input.RegisterTrigger(new InputTrigger(new KeyPressedCondition(Keys.S, true), new InputAction(
                delegate(GameTime time) {
                    car.Translation += car.WorldTransform.GetRotationVector(2) * speed * (float) time.ElapsedTimeInSeconds;
                }
            )));

            //Add another point light to the scene
            PointLight pl = new PointLight();
            pl.Attenuate = false;
            pl.Position = new Vector3(0, 0, 50);
            car.AddLight(pl);
        }

        //During the update process, we check the triggers and process any input. Typically
        //this is done before updating the geometric state of your objects (e.g. calle Spatial.Update()),
        //since your input may change that state.
        protected override void OnUpdate(GameTime time) {
            input.CheckTriggers(time);
        }
    }
}
